﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
using static System.Convert;
using static System.Security.Cryptography.Aes;
using static System.Text.Encoding;

namespace CommLib.Security
{
    public class AesEncrypt
    {
        /// <summary>
        /// 密钥(16位)
        /// </summary>
        private readonly string _key;

        /// <summary>
        /// 偏移量(16位)
        /// </summary>
        private readonly string _iv;

        /// <summary>
        /// AES加解密
        /// </summary>
        /// <param name="key">密钥(16位)</param>
        /// <param name="iv">偏移量(16位)</param>
        public AesEncrypt(string key, string iv)
        {
            _key = key;
            _iv = iv;
        }
        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="encryptString">要加密的字符串</param>
        /// <param name="cipherMode">要加密的密码块的模式（默认CBC）</param>
        /// <param name="paddingMode">填充模式（默认Zero）</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="CryptographicException"></exception>
        /// <exception cref="System.Text.EncoderFallbackException"></exception>
        /// <returns></returns>
        public async Task<Tuple<bool,string>> Encrypt(string encryptString, CipherMode cipherMode = CipherMode.CBC, PaddingMode paddingMode = PaddingMode.Zeros)
        {
            if (string.IsNullOrEmpty(encryptString))
                return new Tuple<bool, string>(false, "要加密的字符串不能为空");
            if (!VerifyKey())
                return new Tuple<bool, string>(false, "Key错误，确认是否为16位");
            if (!VerifyIv())
                return new Tuple<bool, string>(false, "IV错误，确认是否为16位");
            byte[] encrypted;
            using (var aesAlg = Create())
            {
                if (aesAlg == null) return null;
                aesAlg.Key = UTF8.GetBytes(_key);
                aesAlg.IV = UTF8.GetBytes(_iv);
                aesAlg.Mode = cipherMode;
                aesAlg.Padding = paddingMode;
                
                var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
                using (var msEncrypt = new MemoryStream())
                {
                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (var swEncrypt = new StreamWriter(csEncrypt))
                        {
                            await swEncrypt.WriteAsync(encryptString);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }
            return new Tuple<bool, string>(true, ToBase64String(encrypted, 0, encrypted.Length));
        }

        /// <summary>
        /// Aes解密
        /// </summary>
        /// <param name="decryptString">要解密的字符串</param>
        /// <param name="cipherMode">要加密的密码块的模式（默认CBC）</param>
        /// <param name="paddingMode">填充模式（默认Zero）</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="FormatException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="CryptographicException"></exception>
        /// <exception cref="System.Text.EncoderFallbackException"></exception>
        /// <returns></returns>
        public async Task<Tuple<bool, string>> Decrypt(string decryptString, CipherMode cipherMode = CipherMode.CBC, PaddingMode paddingMode = PaddingMode.Zeros)
        {
            if (string.IsNullOrEmpty(decryptString))
                return new Tuple<bool, string>(false, "要解密的字符串不能为空");
            if (!VerifyKey())
                return new Tuple<bool, string>(false, "Key错误，确认是否为16位");
            if (!VerifyIv())
                return new Tuple<bool, string>(false, "IV错误，确认是否为16位");
            string plaintext;
            using (var aesAlg = Create())
            {
                if (aesAlg == null) return null;
                aesAlg.Key = UTF8.GetBytes(_key);
                aesAlg.IV = UTF8.GetBytes(_iv);
                aesAlg.Mode = cipherMode;
                aesAlg.Padding = paddingMode;
                var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                
                using (var msDecrypt = new MemoryStream(FromBase64String(decryptString)))
                {
                    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (var srDecrypt = new StreamReader(csDecrypt))
                        {
                            plaintext = await srDecrypt.ReadToEndAsync();
                        }
                    }
                }
            }
            return new Tuple<bool, string>(true, plaintext);

        }

        private bool VerifyKey() => _key.Length == 16;

        private bool VerifyIv() => _iv.Length == 16;
    }
}
